package com.electronic.utils;

import com.alibaba.fastjson.JSON;
import com.electronic.utils.cert.CAPIStyle;
import com.electronic.utils.cert.CertNames;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: IFCA
 * @Description: 自定义证书工具类
 */
public class ICertUtil {
    private static final Logger log = LoggerFactory.getLogger(ICertUtil.class);
    public static final String BC = "BC";

    /**
     * 获取证书名称
     */
    public static CertNames getCertNames(String name) {
        X500Name x500Name = new X500Name(CAPIStyle.INSTANCE, name);
        CertNames names = new CertNames();
        RDN[] var3 = x500Name.getRDNs();
        int var4 = var3.length;

        for (int var5 = 0; var5 < var4; ++var5) {
            RDN rdn = var3[var5];
            String typeName = CAPIStyle.getTypeName(rdn.getFirst().getType());
            String value = rdn.getFirst().getValue().toString();
            Vector vector;
            if (names.containsKey(typeName)) {
                vector = (Vector) names.get(typeName);
                vector.add(value);
            } else {
                vector = new Vector();
                vector.add(value);
                names.put(typeName, vector);
            }
        }

        return names;
    }

    /**
     * 将签章者证书字符串转证书对象
     */
    public static X509Certificate generateCertificate(byte[] encoded) throws CertificateException {
        return generateCertificate((InputStream) (new ByteArrayInputStream(encoded)));
    }

    /**
     * 将签章者证书数据流转证书对象
     */
    public static X509Certificate generateCertificate(InputStream inStream) throws CertificateException {
        try {
            if (!((InputStream) inStream).markSupported()) {
                inStream = new BufferedInputStream((InputStream) inStream);
            }

            ((InputStream) inStream).mark(10);
            int tag = ((InputStream) inStream).read();
            if (tag == -1) {
                return null;
            } else if (tag != 48) {
                ((InputStream) inStream).reset();
                return readPEMCertificate((InputStream) inStream);
            } else if (((InputStream) inStream).read() == 128) {
                ((InputStream) inStream).reset();
                return readPKCS7Certificate((InputStream) inStream);
            } else {
                ((InputStream) inStream).reset();
                return readDERCertificate((InputStream) inStream);
            }
        } catch (Exception var2) {
            throw new CertificateException(var2.toString());
        }
    }

    /**
     * 读取PEM证书
     */
    private static X509Certificate readPEMCertificate(InputStream inStream) throws IOException, CertificateException, NoSuchProviderException {
        StringBuffer pemBuf = new StringBuffer();

        String line;
        while ((line = readLine(inStream)) != null) {
            if (!line.equals("-----BEGIN CERTIFICATE-----") && !line.equals("-----BEGIN X509 CERTIFICATE-----") && !line.equals("-----END CERTIFICATE-----") && !line.equals("-----END X509 CERTIFICATE-----")) {
                pemBuf.append(line);
            }
        }

        if (pemBuf.length() != 0) {
            ByteArrayInputStream bIn = new ByteArrayInputStream(Base64Utils.decodeFromString(pemBuf.toString()));
            return readDERCertificate(bIn);
        } else {
            return null;
        }
    }

    /**
     * 读取PKCS7证书
     */
    private static X509Certificate readPKCS7Certificate(InputStream in) throws IOException, CertificateException, NoSuchProviderException {
        ASN1InputStream dIn = new ASN1InputStream(in);
        ASN1Sequence seq = (ASN1Sequence) dIn.readObject();
        org.bouncycastle.asn1.x509.Certificate certificate = org.bouncycastle.asn1.x509.Certificate.getInstance(seq);
        return readDERCertificate(new ByteArrayInputStream(certificate.getEncoded()));
    }

    /**
     * 读取DER证书
     */
    private static X509Certificate readDERCertificate(InputStream inStream) throws IOException, CertificateException, NoSuchProviderException {
        return (X509Certificate) CertificateFactory.getInstance("X.509", "BC").generateCertificate(inStream);
    }

    /**
     * 读取数据内容
     */
    private static String readLine(InputStream inStream) throws IOException {
        StringBuffer l = new StringBuffer();

        int c;
        while ((c = inStream.read()) != 10 && c >= 0) {
            if (c != 13) {
                l.append((char) c);
            }
        }

        return l.length() == 0 && c < 0 ? null : l.toString();
    }

    /**
     * 获取证书主题
     */
    public static String getCertAlias(X509Certificate x509Cert) {
        Principal subjectDN = x509Cert.getSubjectDN();
        return getCertAlias(subjectDN);
    }

    /**
     * 获取证书主题
     */
    public static String getCertAlias(Principal principal) {
        CertNames names = getCertNames(principal.getName());
        return getCertAlias(names);
    }

    /**
     * 获取证书主题
     */
    public static String getCertAlias(CertNames names) {
        return names.getItem("CN") != null ? names.getItem("CN") : (String) ((Vector) ((Map.Entry) names.entrySet().iterator().next()).getValue()).get(0);
    }

    static {
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

    }

    /**
     * 从DN字符串中提取O项值
     *
     * @param dn DN字符串
     * @return O项值，如果不存在则返回null
     */
//    public static String getOrganizationFromDN(String dn) {
//        String[] dnComponents = dn.split(",");
//        for (String component : dnComponents) {
//            if (component.trim().startsWith("O=")) {
//                // 去掉"O="前缀并返回组织名称
//                return component.trim().substring(2);
//            }
//        }
//        // 如果没有找到O项，则返回null
//        return null;
//    }

    /**
     * 从DN字符串中提取O项值
     *
     * @param dn DN字符串
     * @return 构建为MAP
     */
    public static Map<String, String> convertToKeyValues(String dn) {
        Map<String, String> keyValuePairs = new HashMap<>();
        // 使用正则表达式匹配DN字符串中的键值对
        Pattern pattern = Pattern.compile("(\\w+)=(.*?)(?=,\\w+=|$)");
        // 创建一个Matcher对象
        Matcher matcher = pattern.matcher(dn);
        // 循环匹配
        while (matcher.find()) {
            String key = matcher.group(1).trim();
            String value = matcher.group(2).trim();
            keyValuePairs.put(key, value);
        }
        // 返回结果
        return keyValuePairs;
    }


    /**
     * 从 X.509 证书中提取授权秘钥标识符
     *
     * @param certificate X.509 证书对象
     * @return 授权秘钥标识符字节数组，如果不存在则返回 null
     */
    public static byte[] getAuthorityKeyIdentifier(X509Certificate certificate) {
        byte[] extVal = certificate.getExtensionValue(Extension.authorityKeyIdentifier.getId());
        if (extVal == null) {
            return null;
        }

        ASN1Primitive asn1Primitive = null;
        try {
            asn1Primitive = ASN1OctetString.fromByteArray(extVal);
            ASN1OctetString octetString = ASN1OctetString.getInstance(asn1Primitive);
            AuthorityKeyIdentifier authkeyid = AuthorityKeyIdentifier.getInstance(octetString.getOctets());
            return authkeyid.getKeyIdentifier();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从 X.509 证书中提签章者密钥标识符
     *
     * @param certificate X.509 证书对象
     * @return 签章者密钥标识符字节数组，如果不存在则返回 null
     */
    public static byte[] getSubjectKeyIdentifier(X509Certificate certificate) {
        byte[] extVal = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId());
        if (extVal == null) {
            return null;
        }

        ASN1Primitive asn1Primitive = null;
        try {
            asn1Primitive = ASN1OctetString.fromByteArray(extVal);
            ASN1OctetString octetString = ASN1OctetString.getInstance(asn1Primitive);
            SubjectKeyIdentifier authkeyid = SubjectKeyIdentifier.getInstance(octetString.getOctets());
            return authkeyid.getKeyIdentifier();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param seq 签名数据结构对象
     * @Description 获取签名算法
     */
    public static String findSignatureOid(ASN1Sequence seq) {
        //签名算法OID
        String oid = null;
        //遍历序列中的每个元素，找到第一个Algorithmidentifier类型的对象，用来判断签名使用的国际标准还是国内标准
        for (int i = 0; i < seq.size(); i++) {
            ASN1Encodable obj = seq.getObjectAt(i);
            if (obj instanceof AlgorithmIdentifier) {
                oid = ((AlgorithmIdentifier) obj).getAlgorithm().toString();
                break;
            } else if (obj instanceof ASN1ObjectIdentifier) {
                AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier((ASN1ObjectIdentifier) obj);
                oid = algorithmIdentifier.getAlgorithm().toString();
                break;
            }
        }
        return oid;
    }

    /**
     * 验证签名证书的秘钥用法是否正确
     *
     * @param cert X509Certificate 对象
     * @return 如果秘钥用法正确，则返回 true；否则返回 false
     */
    public static boolean isValidKeyUsage(X509Certificate cert) {
        boolean[] keyUsage = cert.getKeyUsage();
        if (keyUsage == null) {
            return false;
        }
        //第 0 个元素为 true 表示该证书的公钥可用于数字签名
        //第 1 个元素为 false ，通常这个位置对应的是非否认（Non-repudiation）用途，这表示此证书的公钥不可用于提供不可否认性的相关操作。
        //第 2 个元素为 true 意味着公钥可用于密钥加密
        log.error("证书秘钥用法：" + JSON.toJSONString(keyUsage));
        if (keyUsage[0]) {
            return true;
        }
        return false;
    }
}
